import os
import sys
import shlex
import getpass
import socket
import signal
import subprocess
import platform
from func import *

build_in_cmds = {}


def tokenize(string):
    return shlex.split(string)


def preprocess(tokens):
    processed_token = []
    for token in tokens:
        if token.startswith('$'):
            processed_token.append(os.getenv(token[1:]))
        else:
            processed_token.append(token)
    return processed_token


def handler_kill(signum, frame):
    raise OSError("Killed")


def execute(cmd_tokens):
    with open(HISTORY_PATH, 'a') as history_file:
        history_file.write(' '.join(cmd_tokens) + os.linesep)
    if cmd_tokens:
        cmd_name = cmd_tokens[0]
        cmd_args = cmd_tokens[1:]
        if cmd_name in build_in_cmds:
            return build_in_cmds[cmd_name](cmd_args)
        signal.signal(signal.SIGINT, handler_kill)
        if platform.system() != "Windows":
            p = subprocess.Popen(cmd_tokens)
            p.communicate()
        else:
            command = ""
            command = ' '.join(cmd_tokens)
            os.system(command)
    return SHELL_STATUS_RUN


def display_cmd_prompt():
    user = getpass.getuser()
    hostname = socket.gethostname()
    cwd = os.getcwd()
    base_dir = os.path.basename(cwd)
    home_dir = os.path.expanduser('~')
    if cwd == home_dir:
        base_dir = '~'
    if platform.system() != 'Windows':
        sys.stdout.write("[\033[1;33m%s\033[0;0m@%s \033[1;36m%s\033[0;0m] $ " % (user, hostname, base_dir))
    else:
        sys.stdout.write("[%s@%s %s]$ " % (user, hostname, base_dir))
    sys.stdout.flush()


def ignore_signals():
    if platform.system() != 'Windows':
        signal.signal(signal.SIGTSTP, signal.SIG_IGN)
    signal.signal(signal.SIGINT, signal.SIG_IGN)


def register_command(name, func):
    '''
     注册命令　使命令与相应的处理函数建立映射关系
     @param cmd 命令名
     @param func 函数名
     '''
    build_in_cmds[name] = func


def init():
    '''
    注册所有命令
    '''
    register_command('cd', cd)
    register_command('exit', exit)
    register_command('getenv', getenv)
    register_command('history', history)


def shell_loop():
    status = SHELL_STATUS_RUN

    while status == SHELL_STATUS_RUN:
        # 打印命令提示符
        display_cmd_prompt()

        # 忽略ctrl+c ctrl+z信号
        ignore_signals()

        try:
            # 读取命令
            cmd = sys.stdin.readline()

            # 解析命令
            # 将命令进行拆分返回一个列表
            cmd_token = tokenize(cmd)

            # 预处理函数
            # 将命令中的环境变量使用真实值进行替换
            # 比如将　$HOME 这样的变量替换为实际值
            cmd_tokens = preprocess(cmd_token)

            # 执行命令，并返回shell的状态
            status = execute(cmd_tokens)
        except:
            # sys.exc_info 函数返回一个包含三个值的元祖(type,value,traceback)
            # 这三个值产生于最近一次的处理的异常
            # 我们只获取中间的值
            _, err, _ = sys.exc_info()
            print(err)


def main():
    # 在进行循环之前必须注册处理函数与命令的映射关系
    init()

    # 处理命令的主程序
    shell_loop()


if __name__ == "__main__":
    main()
